public class Lcd extends Processor {
    int port, tris;
    private final static int CLEAR_LCD         = 0x01; /* Clear Display                        */
    private final static int RETURN_HOME       = 0x02; /* Cursor to Home position              */
    private final static int ENTRY_MODE        = 0x06; /* Normal entry mode                    */
    private final static int ENTRY_MODE_SHIFT  = 0x07; /* - with shift                         */
    private final static int SYSTEM_SET_8_BIT  = 0x38; /* 8 bit data mode 2 line ( 5x7 font )  */
    private final static int SYSTEM_SET_4_BIT  = 0x28; /* 4 bit data mode 2 line ( 5x7 font )  */
    private final static int DISPLAY_ON        = 0x0c; /* Switch ON Display                    */
    private final static int DISPLAY_OFF       = 0x08; /* Cursor plus blink                    */
    private final static int SET_DD_LINE1      = 0x80; /* Line 1 position 1                    */
    private final static int SET_DD_LINE2      = 0xC0; /* Line 2 position 1                    */
    private final static int SET_DD_RAM        = 0x80; /* Line 1 position 1                    */
    private final static int WRITE_DATA        = 0x00; /* With RS = 1                          */
    private final static int CURSOR_ON         = 0x0E; /* Switch Cursor ON                     */
    private final static int CURSOR_OFF        = 0x0C; /* Switch Cursor OFF                    */

    private final static int E   = 3;
    private final static int RW  = 2;
    private final static int RS  = 1;
    private final static int DB4 = 4;
    private final static int DB5 = 5;
    private final static int DB6 = 6;
    private final static int DB7 = 7;
    

    public Lcd(int port, int tris) {
	this.port = port;
	this.tris = tris;

	_setMemory(tris, 0); // outputs
	_setMemory(port, 0);

	msDelay(30);                        /* Power up delay */

	sendCmdByte(SYSTEM_SET_8_BIT>>4);
	msDelay(5);                        /* Power up delay */
	sendCmdByte(SYSTEM_SET_8_BIT>>4);
	msDelay(5);                        /* Power up delay */
	sendCmdByte(SYSTEM_SET_8_BIT>>4);

	msDelay(5);                        /* Power up delay */
	sendCmdByte(SYSTEM_SET_4_BIT>>4);

	sendCmd(SYSTEM_SET_4_BIT);

	//sendCmd( display_off );
	sendCmd(DISPLAY_ON);
	sendCmd(CLEAR_LCD);
	sendCmd(ENTRY_MODE);
	sendCmd(SET_DD_RAM);
    }

    void setDataLines(int data) {
	if ((data & 1) != 0) _setBit(port, DB4);
	else _clearBit(port, DB4);
	if ((data & 2) != 0) _setBit(port, DB5);
	else _clearBit(port, DB5);
	if ((data & 4) != 0) _setBit(port, DB6);
	else _clearBit(port, DB6);
	if ((data & 8) != 0) _setBit(port, DB7);
	else _clearBit(port, DB7);
    }

    void sendCmdByte(int cmd) {
	msDelay(5);
	setDataLines(cmd);
	_clearBit(port, RW);
	_clearBit(port, RS);
	toggle();
    }

    void sendCmd(int cmd) {
	waitNotBusy();
	setDataLines(cmd>>4);
	_clearBit(port, RW);
	_clearBit(port, RS);
	toggle();
	setDataLines(cmd);
	toggle();
    }

    public void sendChar(int ch) {
	waitNotBusy();
	setDataLines(ch>>4);
	_clearBit(port, RW);
	_setBit(port, RS);
	toggle();
	setDataLines(ch);
	toggle();
	_clearBit(port, RS);
    }

    private void waitNotBusy() {
	int result = 0x80;

	_setBit(port, RW);
	_clearBit(port, RS);
	
	_setBit(tris, DB4);
	_setBit(tris, DB5);
	_setBit(tris, DB6);
	_setBit(tris, DB7);

	while ((result & 0x80) != 0) {
	    toggle();
	    result = _getMemory(port)<<4;
	    toggle();
	    result |= _getMemory(port);
	    _delay(100);
	    _asm("    clrwdt");
	}

	_clearBit(tris, DB4);
	_clearBit(tris, DB5);
	_clearBit(tris, DB6);
	_clearBit(tris, DB7);
	_clearBit(port, RW);
    }

    public int getStatus() {
	int result;

	_setBit(port, RW);
	_clearBit(port, RS);
	
	_setBit(tris, DB4);
	_setBit(tris, DB5);
	_setBit(tris, DB6);
	_setBit(tris, DB7);

	toggle();
	result = _getMemory(port)<<4;
	toggle();
	result |= _getMemory(port);

	_clearBit(tris, DB4);
	_clearBit(tris, DB5);
	_clearBit(tris, DB6);
	_clearBit(tris, DB7);
	_clearBit(port, RW);

	return result;
    }

    public void msDelay(int ms) {
	for (int i = 0; i < ms; i++) {
	    _delay(1000);
	    _asm("    clrwdt");
	}
    }

    public void clear() {
	sendCmd(CLEAR_LCD);
    }

    public void line2() {
	sendCmd(SET_DD_LINE2);
    }

    private void toggle() {
	_setBit(port, E);
	_delay(10);
	_clearBit(port, E);
	_delay(10);
    }
}
